#include "comm.hpp"

bool createpipe(std::string path)
{
    umask(0);
    int ret = mkfifo(path.c_str(),0666);
    if(ret < 0)
    {
        return false;
    }
    return true;
}

void getShmMessage(int shmid, const char *start)
{

    shmid_ds ds;
    shmctl(shmid, IPC_STAT, &ds);
    printf("共享内存,size:%d,pid:%d", ds.shm_segsz, ds.shm_lpid);
    printf("client say : %s\n", start);
    // sleep(1);
}

int main()
{
    bool r = createpipe(NAME_PIPE);//创建命名管道
    assert(r);
    (void)r;

    key_t k = getKey();
    printf("key : 0x%x\n", k);
    int shmid = createShm(k);
    printf("shmid : %d\n", shmid);

    // 关联
    char *start = (char *)attchShm(shmid);
    printf("attch success, address start%p\n", start);

    // 使用
    // sleep(5);
    int rfd = open(NAME_PIPE, O_RDONLY);
    assert(rfd > 0);
    while (true)
    {
        int num = 0;
        ssize_t s = read(rfd,&num,sizeof(num));
        assert(s >= 0);
        if (s == 4 && num == 1)
        {
            printf("server revc message...\n");
            getShmMessage(shmid, start);
        }
    }

    // 到这里可以不用这么粗暴
    // 可以去关联，让其他进程可以使用这个共享内存(去关联并不是删除内存)
    detachShm(start);
    sleep(5);

    close(rfd);

    delShm(shmid); // 谁创建，谁删除
    return 0;
}

///////////////////////////system V(共享内存)////////////////////////////////////

// system V共享(IPC最快)
// 共享内存的特点：
// 优点：所有进程间通信，速度最快的！(能大大减少数据的拷贝次数)
// 同样的代码，如果用管道来实现？
// 综合考虑管道和共享内存，考虑键盘输入，和显示器输出
// 共享内存有几次拷贝？2+2
// 管道呢？ 4+2 因为多两步拷贝到缓冲区

// 共享内存的缺点：不给我进行同步和互斥的操作，没有对数据做任何保护！(如：客户端发的慢，服务端读的快。导致服务端一直读的都是老的数据)
// 对共享内存进行保护，要如何实现？？ --- 可以用匿名管道

// 1. 共享内存的原理
/*1.1操作系统给我们一些接口，让我们能在内存中申请一块空间（能让所有进程同时看见）
  1.2将创建好的内存映射进进程的地址空间（进程喝共享内存进行挂接）
  1.3未来不想通信了
    a.取消进程喝内存的映射关系（去关联）
    b.释放内存（释放共享内存）
理解：
    a.进程间通信，是专门设计的，用来IPC
    b.共享内存是一种通信方式。所有想通信的进程，都可以用
    c.OS中一定可能会存在很多的共享内存（我在通信，别人也要通信） --> OS也要管理起来，先描述再组织 -> 共享内存 = 物理内存块 + 共享内存的相关属性
    c.1 : 为什么malloc(1024) -> free(p) ? 因为我申请1024个字节，OS要开辟1024大一点点的空间，这个空间拿来放free结束的信息。
*/
// 2.共享内存的概念
// 通过让不同的进程，看到同一个内存块的方式：共享内存！

///////////////////////////////////接口认识：///////////////////////////////////

// #include <sys/ipc.h>
// #include <sys/shm.h>                                    这个还需要权限！否则挂接失败
// int shmget(key_t key, size_t size, int shmflg);   shmflg(IPC_CREAT : 如果想要创建的内存不存在，创建之，如果存在，获取之)  ---> 就是传个0(标记位)
//                       共享内存的大小                     (IPC_EXCL  : 1.无法单独使用
//                                                                      2.IPC_CREAT|IPC_EXCL 如果不存在，创建之，如果存在，就出错返回)
//                                                                        (给用户：如果创建成功，一定是一个新的shm！)
// 返回值：是共享内存的标识符
// key_t key:是什么不重要，能进行唯一性标识最重要（可以用ftok()这个函数获取：能返回一个唯一值）
// key_t ftok(const char *pathname, int proj_id);   两个进程同一个算法，得出同一个key

// 创建共享内存的时候，如何保证共享内存在系统中是唯一的？ key!
// 只要让另一个进程也看到同一个key就能知道在哪通信了
// key在哪？？ -> 在共享内存的相关属性中

// #include <sys/ipc.h>
// #include <sys/shm.h>
// int shmctl(int shmid, int cmd, struct shmid_ds *buf); 共享内存的控制：其中包含内存删除

// struct shmid_ds                （暴漏给我的一部分状态，可获取）
// {
//     struct ipc_perm shm_perm; /* Ownership and permissions */
//     size_t shm_segsz;         /* Size of segment (bytes) */
//     time_t shm_atime;         /* Last attach time */
//     time_t shm_dtime;         /* Last detach time */
//     time_t shm_ctime;         /* Last change time */
//     pid_t shm_cpid;           /* PID of creator */
//     pid_t shm_lpid;           /* PID of last shmat(2)/shmdt(2) */
//     shmatt_t shm_nattch;      /* No. of current attaches */
//     ...
// };

// 创建好共享内存后，需要把进程喝进程关联起来
//  #include <sys/types.h>
//  #include <sys/shm.h>
//  void *shmat(int shmid, const void *shmaddr, int shmflg);这个共享内存的起始地址通过页表返回回来！
// 只用关心shmid就可以了。
// 返回值：等价于以前用的malloc(成功的话是返回该共享内存的起始地址)

// 去关联
//  #include <sys/types.h>
//  #include <sys/shm.h>
//  int shmdt(const void *shmaddr);

////////////////////////////////查看IPC资源//////////////////////////////////////

// shmid(用户级)
// key(内核级)

// 如何查看IPC资源  --> ipcs -m/-q/-s(-m:memory)(消息队列)(信号量)
// 要删除共享内存 ->    ipcrm -m [shmid]
// 共享内存的生命周期是随OS的，不是随进程的！！（以前malloc或者new，进程结束，内存也会自动释放）（system V的共性）